﻿using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace Engine.Graphics
{
    public class RenderQueue
    {
        #region Public Fields

        public DebugInfo debugInfo;
        public bool displayDebugInfo = false;

        #endregion Public Fields

        #region Private Fields

        private List<RenderQueueItem> renderableItems;
        private Stopwatch sw;

        #endregion Private Fields

        #region Public Constructors

        public RenderQueue(Graphic engine)
        {
            this.sw = new Stopwatch();
            sw.Start();
            this.renderableItems = new List<RenderQueueItem>();

            if (this.displayDebugInfo)
            {
                this.debugInfo = new DebugInfo(engine);
                AddRenderableObject(this.debugInfo, 0);
            }
        }

        #endregion Public Constructors

        #region Public Methods

        public void AddRenderableObject(IRenderableObject renderableObject, int priority)
        {
            if (renderableObject.ToString().Contains("RoadV2"))
            { }

            this.renderableItems.Add(new RenderQueueItem() { RenderStepPriority = priority, RenderObject = renderableObject });
            this.renderableItems.Sort();
        }

        public void RemoveRenderableObject(IRenderableObject renderableObject)
        {
            for (int i = 0; i < this.renderableItems.Count; i++)
            {
                if (this.renderableItems[i].RenderObject == renderableObject)
                {
                    this.renderableItems.RemoveAt(i);
                    i--;
                }
            }
        }

        public void Render()
        {
            sw.Restart();

            if (debugInfo != null)
                debugInfo.UpdateListOfRenderObjects();

            int renderNumberOfObjects = 0;

            for (int i = 0; i < this.renderableItems.Count; i++)
            {
                IRenderableObject renderObjects = this.renderableItems[i].RenderObject;
                int renderStep = this.renderableItems[i].RenderStepPriority;

                renderNumberOfObjects++;

                long timeStart = sw.ElapsedMilliseconds;

#if DEBUG
                if (SharpDX.Configuration.EnableObjectTracking)
                    Graphic.ListActiveSharpDxObjects("Render before " + renderObjects.ToString());
#endif
                renderObjects.Render(renderStep);

#if DEBUG
                if (SharpDX.Configuration.EnableObjectTracking)
                    Graphic.ListActiveSharpDxObjects("Render after " + renderObjects.ToString());
#endif
                long timeRemains = sw.ElapsedMilliseconds - timeStart;

                if (debugInfo != null)
                    this.debugInfo.AddRenderTime(renderObjects.ToString(), (int)timeRemains);
            }
        }

        #endregion Public Methods

        #region Internal Methods

        internal void Dispose()
        {
            for (int i = 0; i < this.renderableItems.Count; i++)
            {
                this.renderableItems[i].RenderObject.Dispose();
            }
        }

        #endregion Internal Methods

        #region Private Structs

        private struct RenderQueueItem : IComparable
        {
            #region Public Properties

            public IRenderableObject RenderObject { get; set; }

            public int RenderStepPriority { get; set; }

            #endregion Public Properties

            #region Public Methods

            public int CompareTo(object obj)
            {
                if (obj.GetType() == typeof(RenderQueueItem))
                {
                    RenderQueueItem queueItem = (RenderQueueItem)obj;

                    if (queueItem.RenderStepPriority > this.RenderStepPriority)
                        return -1;
                    if (queueItem.RenderStepPriority < this.RenderStepPriority)
                        return 1;
                }
                return 0;
            }

            #endregion Public Methods
        }

        #endregion Private Structs
    }
}